package nu.zoom.swing.field;

import javax.swing.text.Document;

/**
 * A field that performs validation when the backing document changes.
 * 
 */
public abstract class ValidatingField extends DocumentListeningField {
	private static final long serialVersionUID = 8202027952066644449L;

	public ValidatingField() {
		super();
	}

	public ValidatingField(Document doc, String text, int columns) {
		super(doc, text, columns);
	}

	public ValidatingField(int columns) {
		super(columns);
	}

	public ValidatingField(String text, int columns) {
		super(text, columns);
	}

	public ValidatingField(String text) {
		super(text);
	}

	/**
	 * This method is overridden from the base class to call a validation method
	 * when the document changes.
	 */
	@Override
	protected void documentChanged() {
		ValidationResult result = validateDocument();
		if (result == null) {
			throw new NullPointerException("Validation result may not be null");
		}
		if (result.passed) {
			validationPassed();
		} else {
			validationFailed(result.message);
		}
	}

	/**
	 * Subclasses shoudl perfomr a validation of the field content and report
	 * the result of the validation. It is not legal to return null, this will
	 * cause NPE.
	 */
	protected abstract ValidationResult validateDocument();

	/**
	 * This method will be called if the field validation passed.
	 * 
	 * @param message
	 *            The message from the validation result.
	 */
	protected abstract void validationFailed(String message);

	/**
	 * This method will be called if validation passed, that is, the field value
	 * is valid.
	 * 
	 */
	protected abstract void validationPassed();

	/**
	 * The result of the validation.
	 */
	public static class ValidationResult {
		public static ValidationResult PASSED = new ValidationResult(true, null);

		private boolean passed = false;

		private String message;

		/**
		 * Field validation result.
		 * 
		 * @param passed
		 *            Set to true if the field passed validation, false if the
		 *            field is invalid.
		 * @param message
		 *            An informative message describing why the validation
		 *            passed. The purpose of this message is to inform the user
		 *            what is wrong with the field.
		 */
		public ValidationResult(boolean passed, String message) {
			super();
			this.passed = passed;
			this.message = message;
		}

		/**
		 * Create a result indicating a failed validation.
		 * 
		 * @see #ValidationResult(boolean, String)
		 * @param message
		 */
		public ValidationResult(String message) {
			this(false, message);
		}

		/**
		 * Create a result indicating a failed validation. Will use the
		 * localized message on the exception as the validation message.
		 * 
		 * @see #ValidationResult(boolean, String)
		 * @see Throwable#getLocalizedMessage()
		 * @param message
		 */
		public ValidationResult(Exception message) {
			this(false, message.getLocalizedMessage());
		}
	}

}
